﻿#include <gl/glut.h>
#include <iostream>
#include <cstdlib>
#include <cmath>
class Point3
{
public:
	Point3();
	~Point3();
	Point3(double, double, double);
	Point3(const Point3& p);
	friend Point3 operator +(const Point3& p0, const Point3& p1);//дЫЫуЗ?жиди
	friend Point3 operator -(const Point3& p0, const Point3& p1);
	friend Point3 operator *(const Point3& p, double scalar);
	friend Point3 operator *(double scalar, const Point3& p);
	friend Point3 operator /(const Point3& p, double scalar);
public:
	double x, y, z;
};
Point3::Point3()
{
	x = y = z = 0;
}
Point3::~Point3()
{}
Point3::Point3(double xx, double yy, double zz)
{
	x = xx; y = yy; z = zz;
}
Point3::Point3(const Point3& p)
{
	x = p.x; y = p.y; z = p.z;
}
Point3 operator +(const Point3& p0, const Point3& p1)
{
	return(Point3(p0.x + p1.x, p0.y + p1.y, p0.z + p1.z));
}
Point3 operator -(const Point3& p0, const Point3& p1)
{
	return(Point3(p0.x - p1.x, p0.y - p1.y, p0.z - p1.z));
}
Point3 operator *(const Point3& p, double scalar)
{
	return(Point3(p.x * scalar, p.y * scalar, p.z * scalar));
}
Point3 operator *(double scalar, const Point3& p)
{
	return(Point3(p.x * scalar, p.y * scalar, p.z * scalar));
}
Point3 operator /(const Point3& p, double scalar)
{
	return(Point3(p.x / scalar, p.y / scalar, p.z / scalar));
}
class BicubicBezierPatch
{
public:
	BicubicBezierPatch();
	~BicubicBezierPatch();
	void ReadControlPoint(Point3 P[4][4]);//读入16个控制点
	void DrawCurvedPatch();//绘制双三次Bezier曲面片
	void DrawControlGrid();//绘制控制网格
	void DrawControlPoint(); //绘制控制点
private:
	void LeftMultiplyMatrix(double M[][4], Point3 CP[][4]);//左乘顶点矩阵
	void RightMultiplyMatrix(Point3 P[][4], double M[][4]);//右乘顶点矩阵
	void TransposeMatrix(double M[][4]);//转置矩阵

public:
	Point3 P[4][4];//三维控制点
};



BicubicBezierPatch::BicubicBezierPatch()
{
}


BicubicBezierPatch::~BicubicBezierPatch()
{
}

void BicubicBezierPatch::ReadControlPoint(Point3 CP[4][4])
{
	for (int i = 0; i < 4; i++)
		for (int j = 0; j < 4; j++)
			this->P[i][j] = CP[i][j];
}

void BicubicBezierPatch::DrawCurvedPatch()
{
	double M[4][4];//系数矩阵Mbe
	M[0][0] = -1; M[0][1] = 3; M[0][2] = -3; M[0][3] = 1;
	M[1][0] = 3; M[1][1] = -6; M[1][2] = 3; M[1][3] = 0;
	M[2][0] = -3; M[2][1] = 3; M[2][2] = 0; M[2][3] = 0;
	M[3][0] = 1; M[3][1] = 0; M[3][2] = 0; M[3][3] = 0;
	Point3 P3[4][4];//曲线计算用控制点数组
	for (int i = 0; i < 4; i++)
		for (int j = 0; j < 4; j++)
			P3[i][j] = P[i][j];
	LeftMultiplyMatrix(M, P3);//数字矩阵左乘三维点矩阵
	TransposeMatrix(M);//计算转置矩阵
	RightMultiplyMatrix(P3, M);//数字矩阵右乘三维点矩阵
	double Step = 0.1;//步长
	double u0, u1, u2, u3, v0, v1, v2, v3;//u，v参数的幂
	glColor3f(1, 1, 0);
	glLineWidth(1);
	glPushMatrix();
	for (double u = 0; u <= 1; u += Step)
	{
		glBegin(GL_LINE_STRIP);
		for (double v = 0; v <= 1; v += Step)
		{
			u3 = u * u * u, u2 = u * u, u1 = u, u0 = 1;
			v3 = v * v * v, v2 = v * v, v1 = v, v0 = 1;
			Point3 pt = (u3 * P3[0][0] + u2 * P3[1][0] + u1 * P3[2][0] + u0 * P3[3][0]) * v3
				+ (u3 * P3[0][1] + u2 * P3[1][1] + u1 * P3[2][1] + u0 * P3[3][1]) * v2
				+ (u3 * P3[0][2] + u2 * P3[1][2] + u1 * P3[2][2] + u0 * P3[3][2]) * v1
				+ (u3 * P3[0][3] + u2 * P3[1][3] + u1 * P3[2][3] + u0 * P3[3][3]) * v0;

			glVertex3d(pt.x, pt.y, pt.z);
		}
		glEnd();
	}

	for (double v = 0; v <= 1; v += Step)
	{
		glBegin(GL_LINE_STRIP);
		for (double u = 0; u <= 1; u += Step)
		{
			u3 = u * u * u; u2 = u * u; u1 = u; u0 = 1; v3 = v * v * v; v2 = v * v; v1 = v; v0 = 1;
			Point3 pt = (u3 * P3[0][0] + u2 * P3[1][0] + u1 * P3[2][0] + u0 * P3[3][0]) * v3
				+ (u3 * P3[0][1] + u2 * P3[1][1] + u1 * P3[2][1] + u0 * P3[3][1]) * v2
				+ (u3 * P3[0][2] + u2 * P3[1][2] + u1 * P3[2][2] + u0 * P3[3][2]) * v1
				+ (u3 * P3[0][3] + u2 * P3[1][3] + u1 * P3[2][3] + u0 * P3[3][3]) * v0;
			glVertex3d(pt.x, pt.y, pt.z);
		}
		glEnd();
	}
	glPopMatrix();
}


void BicubicBezierPatch::LeftMultiplyMatrix(double M[][4], Point3 P[][4])//左乘矩阵M*P
{
	Point3 T[4][4];//临时矩阵
	for (int i = 0; i < 4; i++)
		for (int j = 0; j < 4; j++)
			T[i][j] = M[i][0] * P[0][j] + M[i][1] * P[1][j] + M[i][2] * P[2][j] + M[i][3] * P[3][j];
	for (int i = 0; i < 4; i++)
		for (int j = 0; j < 4; j++)
			P[i][j] = T[i][j];
}

void BicubicBezierPatch::RightMultiplyMatrix(Point3 P[][4], double M[][4])//右乘矩阵P*M
{
	Point3 T[4][4];//临时矩阵
	for (int i = 0; i < 4; i++)
		for (int j = 0; j < 4; j++)
			T[i][j] = P[i][0] * M[0][j] + P[i][1] * M[1][j] + P[i][2] * M[2][j] + P[i][3] * M[3][j];
	for (int i = 0; i < 4; i++)
		for (int j = 0; j < 4; j++)
			P[i][j] = T[i][j];
}

void BicubicBezierPatch::TransposeMatrix(double M[][4])//转置矩阵
{
	double T[4][4];//临时矩阵
	for (int i = 0; i < 4; i++)
		for (int j = 0; j < 4; j++)
			T[j][i] = M[i][j];
	for (int i = 0; i < 4; i++)
		for (int j = 0; j < 4; j++)
			M[i][j] = T[i][j];
}

void BicubicBezierPatch::DrawControlGrid()//绘制控制网格
{
	Point3 P2[4][4];//二维控制点
	for (int i = 0; i < 4; i++)
		for (int j = 0; j < 4; j++)
		{
			P2[i][j].x = P[i][j].x;
			P2[i][j].y = P[i][j].y;
			P2[i][j].z = P[i][j].z;
		}
	glColor3f(0, 1, 0);
	glLineWidth(3);
	glBegin(GL_LINE_STRIP);

	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
			glVertex3d(P2[i][j].x, P2[i][j].y, P2[i][j].z);
	}


	for (int j = 0; j < 4; j++)
	{
		for (int i = 0; i < 4; i++)
			glVertex3d(P2[i][j].x, P2[i][j].y, P2[i][j].z);
	}
	glEnd();
}
void BicubicBezierPatch::DrawControlPoint()//绘制控制
{
	glColor3f(1, 0, 0);
	glPointSize(5);
	glBegin(GL_POINTS);
	for (int i = 0; i < 4; i++)
		for (int j = 0; j < 4; j++)
			glVertex3d(P[i][j].x, P[i][j].y, P[i][j].z);
	glEnd();
}
#define MAX_NUM_POINTS 16 

static GLint width = 800, height = 800;
GLfloat oldx, oldy;
double alpha_x = 0.0;
double alpha_y = 0.0;

static int n = 0;

Point3 P0[4][4];
Point3* P;
BicubicBezierPatch patch0;

void init_controlpoint();
bool mouseFlag = 0;
void init()
{
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-10.0, 10.0, -10.0, 10.0, -10.0, 10);
	glMatrixMode(GL_MODELVIEW);
	// Set the clear color to black
	glClearColor(0.0, 0.0, 0.0, 0.0); //black
	//init_controlpoint();
	P = new Point3[MAX_NUM_POINTS];
}

void  display()
{
	glClear(GL_COLOR_BUFFER_BIT);
	if (mouseFlag)
	{
		init_controlpoint();
		patch0.ReadControlPoint(P0);
		glRotatef(alpha_x, 1, 0, 0);
		glRotatef(alpha_y, 0, 1, 0);

		patch0.DrawControlGrid();
		patch0.DrawCurvedPatch();
		patch0.DrawControlPoint();
	}
	glFlush();
}

void init_controlpoint()
{
	double l = 3, m = 1;
	int n = 0;
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			P0[i][j].x = P[n + j].x;
			P0[i][j].y = P[n + j].y;
			P0[i][j].z = -(l * m + rand() % 3);
		}
		n += 4;
	}

	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
			std::cout << P0[i][j].x << " / " << P0[i][j].y << " / " << P0[i][j].z << std::endl;
		std::cout << "---------\n";
	}
}
void mouse(int button, int state, int x, int y)
{
	float wx, wy;
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
	{
		mouseFlag = true;
		/* Translate screen coordinate system to view coordinate system */
		//wx = (20.0 * (x - 250)) / (float)(width);
		//wy = (20.0 * (height / 2 - y)) / (float)(height);
		wx = -10 + (20.0 * x) / (float)(width);
		wy = (20.0 * (height / 2.0 - y)) / (float)(height);
		/* See if we have room for any more control points */
		if (n == MAX_NUM_POINTS) return;
		/* Save the point */
		P[n].x = wx;
		P[n].y = wy;
		std::cout << "x-y:" << x << " " << y << std::endl;

		std::cout << P[n].x << " ";
		std::cout << P[n].y << std::endl;
		n++;
		/* Draw the point */
		glColor3f(1.0, 0.0, 0.0);
		glPointSize(5.0);
		glBegin(GL_POINTS);
		glVertex3f(wx, wy, 0.0);
		glEnd();
		glFlush();
	}
}

void keyboard(unsigned char key, int x, int y)
{
	switch (key)
	{
	case 'q':
	case 'Q':
		exit(0);
		break;
	case 'c':
	case 'C':   //redraw
		n = 0;
		glutPostRedisplay();
		break;
	case 'e':
	case 'E':  //erase 
		mouseFlag = false;
		glutPostRedisplay();
		break;
	case 'd':
	case 'D':
		glutPostRedisplay();
		break;
	case 'w':
	case 'W':
		alpha_x += 360 * 1.0f / width;
		glutPostRedisplay();
		break;
	case 'z':
	case 'Z':
		alpha_x -= 360 * 1.0f / width;
		glutPostRedisplay();
		break;
	case 's':
	case 'S':
		alpha_y += 360 * 1.0f / height;
		glutPostRedisplay();
		break;
	case 'a':
	case 'A':
		alpha_y -= 360 * 1.0f / height;
		glutPostRedisplay();
		break;
	}
}

void reshape(int w, int h)
{
	glViewport(0, 0, (GLsizei)w, (GLsizei)h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	if (w <= h)
		glOrtho(-10.0, 10.0, -10.0 * (GLfloat)h / (GLfloat)w,
			10.0 * (GLfloat)h / (GLfloat)w, -10.0, 10.0);
	else
		glOrtho(-10.0 * (GLfloat)w / (GLfloat)h,
			10.0 * (GLfloat)w / (GLfloat)h, -10.0, 10.0, -10.0, 10.0);
	glMatrixMode(GL_MODELVIEW);
}
int  main(int  argc, char** argv)
{
	glutInit(&argc, argv);    // Initialize GLUT function callings
	//sets the initial display mode
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	// Set window position, from the left and top corner of the screen, in numbers of pixels 
	glutInitWindowPosition(200, 100);

	// Set window size (width, height) in number of pixels     
	glutInitWindowSize(800, 800);

	// Specify a window creation event 
	glutCreateWindow("Bezier Surface");
	init();	    // Invoke this function for initialization	
	//A window is reshaped
	glutReshapeFunc(reshape);

	// Specify the drawing function that is called when the window 
	glutDisplayFunc(display);         // is created or re-drew


	glutKeyboardFunc(keyboard);
	glutMouseFunc(mouse);

	glutMainLoop();    // Enter the event processing loop

	return 0;          // Indicate normal termination 		
}

